﻿/* ----------------------------------------------------------------------------
 * Copyright (c) Philip Stears 
 *
 * This source code is subject to terms and conditions of the Microsoft Public 
 * License (Ms-PL).
 * 
 * By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 * 
 * To request a copy of the Microsoft Public License, send a request by email
 * to license@connectbasic.net.
 *
 * This notice must not be removed from this software.
 * ---------------------------------------------------------------------------- */

namespace ConnectBasic.Language
{
    using System;
    using System.Windows.Forms;

    /// <summary>
    /// Runtime helpers for the ConnectBasic language.
    /// </summary>
    public static class BasicBuiltins
    {

        /// <summary>
        /// Shows an input box.
        /// </summary>
        /// <param name="prompt">The prompt to show on the input box.</param>
        /// <param name="title">The title to show on the input box.</param>
        /// <returns></returns>
        public static string InputBox(string prompt)
        {
            return Microsoft.VisualBasic.Interaction.InputBox(prompt, string.Empty, null, -1, -1);  
        }

        /// <summary>
        /// Shows an input box.
        /// </summary>
        /// <param name="prompt">The prompt to show on the input box.</param>
        /// <param name="title">The title to show on the input box.</param>
        /// <returns></returns>
        public static string InputBox(string prompt, string title)
        {
            return Microsoft.VisualBasic.Interaction.InputBox(prompt, title, null, -1, -1);
        }

        /// <summary>
        /// Shows a message box.
        /// </summary>
        /// <param name="title">The description to show on the message box.</param>
        public static void MsgBox(string title)
        {
            MessageBox.Show(title);
        }

        /// <summary>
        /// Gets the length of the specified string.
        /// </summary>
        /// <param name="s">The string to get the length of.</param>
        /// <returns></returns>
        public static short Len(string s)
        {
            if (s == null)
                return (short)0;
            else
                return (short)s.Length;
        }

        /// <summary>
        /// Gets the specified number of characters from the left hand side of the given string.
        /// </summary>
        /// <param name="s">The string to get the left hand side of.</param>
        /// <param name="length">The number of characters to return.</param>
        /// <returns></returns>
        public static string Left(string s, short length)
        {
            if (s == null)
                return string.Empty;
            else
                return s.Substring(0, Math.Min(length, s.Length));
        }

        /// <summary>
        /// Gets the specified number of characters from the right hand side of the given string.
        /// </summary>
        /// <param name="s">The string to get the right hand side of.</param>
        /// <param name="length">The number of characters to return.</param>
        /// <returns></returns>
        public static string Right(string s, short length)
        {
            if (s == null)
                return string.Empty;
            else
            {
                length = (short)Math.Min(length, s.Length);
                return s.Substring(s.Length - length, length);
            }
        }

        /// <summary>
        /// Gets the specified number of characters from the middle of the string.
        /// </summary>
        /// <param name="s">The string from which to get the middle.</param>
        /// <param name="start">The index of the character from which to start (starting at 1).</param>
        /// <param name="length">The number of characters to return.</param>
        /// <returns></returns>
        public static string Mid(string s, short start, short length)
        {
            if (s == null)
                return string.Empty;
            else if (start == s.Length)
                return string.Empty;
            else if (start == 0)
                return string.Empty;
            else
            {

                // VBA is 1-based
                start--;
                length = (short)Math.Min(s.Length - start, length);
                return s.Substring(start, length);
            }
        }

        /// <summary>
        /// Gets the remainder of the string.
        /// </summary>
        /// <param name="s">The string from which to get the remainder.</param>
        /// <param name="start">The index of the character from which to start (starting at 1).</param>
        /// <returns></returns>
        public static string Mid(string s, short start)
        {
            if (s == null)
                return string.Empty;
            else if (start == s.Length)
                return string.Empty;
            else if (start == 0)
                return string.Empty;
            else
            {

                // VBA is 1-based
                start--;
                return s.Substring(start);
            }
        }

        /// <summary>
        /// Returns the location of one string inside another.
        /// </summary>
        /// <param name="s">The string to search.</param>
        /// <param name="findStr">The string to find.</param>
        /// <returns>The index of the string to find, or 0 if the string is not found.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Str")]
        public static short InStr(string s, string findStr)
        {
            return InStr(s, findStr, (StringCompareMethod)(-1));
        }

        /// <summary>
        /// Returns the location of one string inside another by searching backwards.
        /// </summary>
        /// <param name="s">The string to search.</param>
        /// <param name="findStr">The string to find.</param>
        /// <returns>The index of the string to find, or 0 if the string is not found.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Str")]
        public static short InStrRev(string s, string findStr)
        {
            return InStrRev(s, findStr, (StringCompareMethod)(-1));
        }

        /// <summary>
        /// Returns the location of one string inside another.
        /// </summary>
        /// <param name="s">The string to search.</param>
        /// <param name="findStr">The string to find.</param>
        /// <param name="comparisonType">An option which controls the way the comparison is performed.</param>
        /// <returns>The index of the string to find, or 0 if the string is not found.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Str")]
        public static short InStr(string s, string findStr, StringCompareMethod comparisonType)
        {
            return InStr((short)1, s, findStr, comparisonType);
        }

        /// <summary>
        /// Returns the location of one string inside another by searching backwards.
        /// </summary>
        /// <param name="s">The string to search.</param>
        /// <param name="findStr">The string to find.</param>
        /// <param name="comparisonType">An option which controls the way the comparison is performed.</param>
        /// <returns>The index of the string to find, or 0 if the string is not found.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Str")]
        public static short InStrRev(string s, string findStr, StringCompareMethod comparisonType)
        {
            if (s == null || findStr == null)
                return 0;
            else
                return InStrRev((short)s.Length, s, findStr, comparisonType);
        }

        /// <summary>
        /// Returns the location of one string inside another.
        /// </summary>
        /// <param name="startIndex">The starting index.</param>
        /// <param name="s">The string to search.</param>
        /// <param name="findStr">The string to find.</param>
        /// <param name="comparisonType">An option which controls the way the comparison is performed.</param>
        /// <returns>The index of the string to find, or 0 if the string is not found.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Str")]
        public static short InStr(short startIndex, string s, string findStr, StringCompareMethod comparisonType)
        {
            if (s == null || findStr == null || startIndex < 1 || startIndex > s.Length)
                return 0;
            else
                return (short)(s.IndexOf(findStr, startIndex - 1, MapCompareType(comparisonType)) + 1);
        }

        /// <summary>
        /// Returns the location of one string inside another by searching backwards.
        /// </summary>
        /// <param name="startIndex">The starting index.</param>
        /// <param name="s">The string to search.</param>
        /// <param name="findStr">The string to find.</param>
        /// <param name="comparisonType">An option which controls the way the comparison is performed.</param>
        /// <returns>The index of the string to find, or 0 if the string is not found.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Str")]
        public static short InStrRev(short startIndex, string s, string findStr, StringCompareMethod comparisonType)
        {
            if (s == null || findStr == null || startIndex < 1 || startIndex > s.Length)
                return 0;
            else
                return (short)(s.LastIndexOf(findStr, startIndex - 1, MapCompareType(comparisonType)) + 1);
        }

        /// <summary>
        /// Trims the given string.
        /// </summary>
        /// <param name="s">The string to trim.</param>
        /// <returns></returns>
        public static string Trim(string s)
        {
            if (s == null)
                return string.Empty;
            else
                return s.Trim();
        }

        /// <summary>
        /// Creates a new COM object given its programmatic identifier.
        /// </summary>
        /// <param name="progId"></param>
        /// <returns></returns>
        public static object CreateObject(string progId)
        {
            return Activator.CreateInstance(Type.GetTypeFromProgID(progId));
        }

        #region Private Helpers

        private static StringComparison MapCompareType(StringCompareMethod compareOption)
        {
            switch (compareOption)
            {
                case (StringCompareMethod)(-1): // Use Option Compare
                    return StringComparison.CurrentCultureIgnoreCase;
                case (StringCompareMethod)0: // Use Binary
                    return StringComparison.Ordinal;
                case (StringCompareMethod)1: // Use Text
                    return StringComparison.CurrentCultureIgnoreCase;
                case (StringCompareMethod)2: // Use Database (for Access)
                    return StringComparison.CurrentCultureIgnoreCase;
                default:
                    return StringComparison.CurrentCultureIgnoreCase;
            }
        }

        #endregion
    }
}
